#include "ftp_users_configuration_reader.h"

#include <iostream>
#include "auth_exception.h"

#include <sys/types.h>
#include <pwd.h>
#include <grp.h>

#include <stdlib.h>

AUTH_NS_START

FtpUsersConfigurationReader::FtpUsersConfigurationReader()
	:m_bIsInitialized(false), m_GroupList(), m_UserList() {
}

FtpUsersConfigurationReader::~FtpUsersConfigurationReader() {
	for(UserListType::const_iterator u = m_UserList.begin(); u != m_UserList.end(); ++u) {
		FtpUser* user = u->second;
		delete user;
	}
	m_UserList.clear();

	for(GroupListType::const_iterator g = m_GroupList.begin(); g != m_GroupList.end(); ++g) {
		FtpGroup* group = g->second;
		delete group;
	}
	m_GroupList.clear();
}

void FtpUsersConfigurationReader::Initialize() {
	if(this->m_bIsInitialized == true)
		return; // Already Initialized. Return.

	FtpUser* ftpUsr;
	FtpGroup* ftpGrp;
	struct passwd* usr;
	struct group* grp;

	// For all the users iterate. Find their groups and add the user and the groups.
	for(usr = getpwent(); usr != NULL; usr = getpwent()) {
		ftpUsr = this->InsertUserToList(usr);
		int groupCount = 0;
		gid_t* groupIds = (gid_t*)malloc(groupCount * sizeof(gid_t));
		if(groupIds == NULL)
			throw AuthException("Malloc - Getting Group List");

		if(getgrouplist(usr->pw_name, usr->pw_gid, groupIds, &groupCount) == -1) {
			// Set to 0 so that we get the number of groups that are present.
			free(groupIds);
			// Reallocate
			groupIds = (gid_t*)malloc(groupCount * sizeof(gid_t));
			if(groupIds == NULL)
				throw AuthException("Malloc - Getting Group List");
			// Get the data. if it still fails, dont process this loop further.
			if(getgrouplist(usr->pw_name,usr->pw_gid,groupIds, &groupCount) == -1) {
				continue;
			}
		}
		
		for(int idx = 0; idx < groupCount; ++idx) {
			grp = getgrgid(groupIds[idx]);
			if(grp != NULL) {
				ftpGrp = this->InsertGroupToList(grp);
				ftpGrp->RegisterUserInGroup(ftpUsr);
			}
		}
		
		free(groupIds);;
	}

	// Iterate through all the groups. This is to add all groups that dont have users to the list.
	for(grp = getgrent(); grp != NULL; grp = getgrent()) {
		ftpGrp = this->InsertGroupToList(grp);
	}

	this->m_bIsInitialized = true;
}

FtpUser* FtpUsersConfigurationReader::InsertUserToList(const passwd* user) {
	UserListType::iterator iter = this->m_UserList.find(user->pw_uid);
	FtpUser* ftpuser = NULL;
	if(iter == this->m_UserList.end()) {
		ftpuser = new FtpUser(user->pw_name, user->pw_uid);
		this->m_UserList[user->pw_uid] = ftpuser;
	} else {
		ftpuser = iter->second;
	}
	return ftpuser;
}

FtpGroup* FtpUsersConfigurationReader::InsertGroupToList(const group* grp) {
	GroupListType::iterator iter = this->m_GroupList.find(grp->gr_gid);
	FtpGroup* ftpGroup = NULL;
	if(iter == this->m_GroupList.end()) {
		ftpGroup = new FtpGroup(grp->gr_name, grp->gr_gid);
		this->m_GroupList[grp->gr_gid] = ftpGroup;
	} else {
		ftpGroup = iter->second;
	}
	return ftpGroup;
}

FtpUser* FtpUsersConfigurationReader::GetUserById(uid_t userId) const {
	UserListType::const_iterator val = this->m_UserList.find(userId);
	FtpUser* usr = NULL;
	if(val != this->m_UserList.end())
		usr = val->second;
	return usr;
	// return this->m_UserList[(uid_t)userId]; - Cant do this as it is a const function and the [] operator is not const;
}

FtpGroup* FtpUsersConfigurationReader::GetGroupById(gid_t groupId) const {
	GroupListType::const_iterator val = this->m_GroupList.find(groupId);
	FtpGroup* grp = NULL;
	if(val != this->m_GroupList.end())
		grp = val->second;
	return grp;
	//return this->m_GroupList[groupId];
}

AUTH_NS_END
